CSRF 是一種網絡攻擊,攻擊者會在使用者已經登入的情況下,執行未授權的操作。攻擊者利用已驗證的 Session (Authenticated Session),即使用者已經透過登入等方式被確認其身份,Server 會讓這個 Session 維持被認證的狀態。攻擊者發送偽造的請求到受害者已經登入的網站,從而在使用者不知情的情況下進行惡意操作,例如修改帳戶資料、轉移資金等。
避免 CSRF 方法:
Strict:只有當請求來自同一個網站(SameSite)時,瀏覽器才會攜帶 Cookie。這意味著 Cross-Site Request 不會攜帶該 Cookie,因此能夠有效防止 CSRF 攻擊。這是最嚴格的模式,但有時可能會限制安全的 Cross-Site 操作。
Lax:允許一些 Cross-Site Request 攜帶 Cookie,但僅限於安全的情況下(如使用 GET 請求進行的網頁訪問)。這個模式在大多數情況下能夠防止 CSRF 攻擊,同時不會破壞使用者的正常操作體驗。
None:Cookie 將在所有情況下都被發送,包括跨站請求。但需要確保 Cookie 是安全的(使用 HTTPS),否則瀏覽器會拒絕。
範例:在 ASP Dotnet Core 將 SameSite 屬性設置為 None。
public void ConfigureServices(IServiceCollection services)
{
services.ConfigureApplicationCookie(options =>
{
options.Cookie.SameSite = SameSiteMode.None; // 將 SameSite 屬性設為 None
options.Cookie.SecurePolicy = CookieSecurePolicy.Always; // 建議在 SameSite = None 時使用 Secure Cookie
});
}
public class MyController : Controller
{
public IActionResult SetCookie()
{
var cookieOptions = new CookieOptions
{
Expires = DateTimeOffset.UtcNow.AddDays(7), // 設 Cookie 過期時間
HttpOnly = true, // 防止 JavaScript 訪問
SameSite = SameSiteMode.None, // 將 SameSite 屬性設置為 None
Secure = true // 僅在 HTTPS 中傳輸 Cookie
};
Response.Cookies.Append("MyCookie", "cookieValue", cookieOptions);
return Ok("Cookie has been set.");
}
}
避免使用可能不安全的 API,如 eval()、setTimeout() 和 setInterval(),特別是在處理來自使用者的輸入時。
使用 Function 建構子(Constructor Function)或帶有任意 JavaScript 程式碼的 Function 調用都應謹慎,因為這些方法可能被利用執行惡意代碼。
範例:
// 不安全寫法
var globalVar = "I am a global variable";
var func = new Function('console.log(globalVar);'); // 沒有訪問到 globalVar
func(); // 會報錯:ReferenceError: globalVar is not defined
// 改善寫法 1:將 globalVar 作為參數傳遞
var globalVar = "I am a global variable";
var func = new Function('globalVar', 'console.log(globalVar);');
func(globalVar); // 輸出:I am a global variable
// 改善寫法 2:使用箭頭函數
var globalVar = "I am a global variable";
const func = () => {
console.log(globalVar); // 輸出:I am a global variable
};
func();
JavaScript 是單一執行緒的語言,污染全域命名空間可能導致競爭條件(Race Condition)和其他安全問題,應該盡量避免將變數設為全域性,使用模組化的方式來封裝變數和邏輯。
範例:Race Condition 通常都出現在非同步操作。
不好的寫法:setTimeout() 是非同步執行,兩個 incrementCounter() 可能幾乎同時讀取 counter 的值,然後都試圖更新它,將導致最後的 counter 值不正確。
let counter = 0;
function incrementCounter() {
const temp = counter;
setTimeout(() => {
counter = temp + 1;
console.log(`Counter: ${counter}`);
}, Math.random() * 1000);
}
// 同時執行兩個遞增操作
incrementCounter();
incrementCounter();
改善寫法 1:使用 Promise。
let counter = 0;
function incrementCounter() {
return new Promise((resolve) => {
const temp = counter;
setTimeout(() => {
counter = temp + 1;
console.log(`Counter: ${counter}`);
resolve();
}, Math.random() * 1000);
});
}
Promise.all([incrementCounter(), incrementCounter()]);
改善寫法 2:使用 async/await。
let counter = 0;
function incrementCounter() {
return new Promise((resolve) => {
const temp = counter;
setTimeout(() => {
counter = temp + 1;
console.log(`Counter: ${counter}`);
resolve();
}, Math.random() * 1000);
});
}
async function run() {
await incrementCounter();
await incrementCounter();
}
run();
確保所有的 API 請求以及靜態資源均使用 HTTPS,防止被攻擊風險。
確保任何敏感數據的傳輸和存儲都經過加密,並避免將敏感數據(如密碼)直接存儲在 Client 端,如 LocalStorage、SessionStorage。
Code Review 檢查清單如下:
這兩天說明了一些 JavaScript 程式碼撰寫與 Code Review 時應該注意的安全性建議,能幫助提升應用的安全性,減少漏洞風險。